/**
    ******************************************************************************
    * @file    main.c 
    * @author  18408107475@163.com
    * @version v1.0
    * @date    2025/04/06
    * @brief   STM32F103 USART1通信示例
    * @details 
    * 功能特性：
    * - 实现USART1串口通信(波特率可配置)
    * - 支持数据接收中断和回显功能
    * - 提供多种数据发送方式(字节/数组/字符串/数字)
    * - 支持printf重定向
    * 
    * 硬件连接：
    * - USART1_TX -> PA9
    * - USART1_RX -> PA10
    * - LED1 -> PB5 (低电平点亮)
    * - LED2 -> PE5 (低电平点亮)
    * - 蜂鸣器 -> PB8 (高电平触发)
    * - 按键 -> PE4 (下降沿触发)
    * 
    * 注意事项：
    * 1. 使用前需确保硬件连接正确
    * 2. 浮空输入引脚需外部上拉/下拉电阻
    * 3. 使用printf需在工程设置中启用MicroLIB
    * 4. 中断服务函数中应避免耗时操作
    ******************************************************************************
**/

/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f10x.h"  // STM32标准外设库
#include <stdio.h>      // 标准输入输出
#include <stdarg.h>     // 可变参数支持

/* 函数声明 ------------------------------------------------------------------*/
void LED_STD_Config(void);      // LED初始化
void BEEP_STD_Config(void);     // 蜂鸣器初始化
void KEY_STD_Config(void);      // 按键初始化
void Delay_us(uint32_t xus);    // 微秒延时
void Delay_ms(uint32_t xms);    // 毫秒延时
void Delay_s(uint32_t xs);      // 秒延时
void USART1_Config(u32 baud);   // 串口初始化
void USART1_IRQHandler(void);   // 串口中断服务函数
void USART1_SendByte(u8 Byte);  // 发送单字节
void USART1_SendArray(u8 *Array, u16 Length); // 发送字节数组
void USART1_SendString(char *String);         // 发送字符串
void USART1_SendNumber(u32 Num, u8 Length);   // 发送数字
void USART1_Printf(char *format, ...);        // 自定义printf

/* 全局变量 ------------------------------------------------------------------*/
// 当前无全局变量

/**
    ******************************************************************************
    * 函数名称：LED_STD_Config
    * 功能描述：初始化LED控制引脚
    * 输入参数：无
    * 返回值  ：无
    * 详细说明：
    * - 配置PB5和PE5为推挽输出
    * - 初始状态：PB5高电平(LED灭)，PE5低电平(LED亮)
    * - 推挽输出可直接驱动LED，无需额外电路
    * 调用示例：
    *   LED_STD_Config();
    ******************************************************************************
**/
void LED_STD_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    /* 开启GPIOB和GPIOE时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE, ENABLE);

    /* 配置PB5为50MHz推挽输出 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    /* 配置PE5为50MHz推挽输出 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_Init(GPIOE, &GPIO_InitStructure);

    /* 设置初始状态 */
    GPIO_SetBits(GPIOB, GPIO_Pin_5);   // PB5高电平，LED灭
    GPIO_ResetBits(GPIOE, GPIO_Pin_5); // PE5低电平，LED亮
}

/**
    ******************************************************************************
    * 函数名称：BEEP_STD_Config
    * 功能描述：初始化蜂鸣器控制引脚
    * 输入参数：无
    * 返回值  ：无
    * 详细说明：
    * - 配置PB8为推挽输出
    * - 初始状态低电平(蜂鸣器关闭)
    * - 适用于有源蜂鸣器(高电平触发)
    * 调用示例：
    *   BEEP_STD_Config();
    ******************************************************************************
**/
void BEEP_STD_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    /* 开启GPIOB时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    /* 配置PB8为50MHz推挽输出 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    /* 初始状态：蜂鸣器关闭 */
    GPIO_ResetBits(GPIOB, GPIO_Pin_8);
}

/**
    ******************************************************************************
    * 函数名称：KEY_STD_Config
    * 功能描述：初始化按键引脚和外部中断
    * 输入参数：无
    * 返回值  ：无
    * 详细说明：
    * - 配置PE4为浮空输入
    * - 设置EXTI4为下降沿触发
    * - 配置NVIC中断优先级(最低)
    * - 需外部上拉电阻，按键按下产生下降沿
    * 调用示例：
    *   KEY_STD_Config();
    ******************************************************************************
**/
void KEY_STD_Config(void)
{
    EXTI_InitTypeDef EXTI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    /* 开启GPIOE时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);

    /* 配置PE4为浮空输入 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOE, &GPIO_InitStructure);

    /* 开启AFIO时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

    /* 将PE4映射到EXTI4 */
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);

    /* 配置EXTI4 */
    EXTI_InitStructure.EXTI_Line = EXTI_Line4;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    /* 配置NVIC */
    NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/**
    ******************************************************************************
    * 函数名称：Delay_us
    * 功能描述：微秒级延时
    * 输入参数：xus - 延时时长(0~1864134 us)
    * 返回值  ：无
    * 详细说明：
    * - 使用SysTick定时器实现精确延时
    * - 基于9MHz时钟(1us=9个时钟周期)
    * - 最大延时受24位计数器限制
    * 注意事项：
    * - 不适用于中断服务函数中调用
    * - 实际延时会有几个时钟周期的误差
    ******************************************************************************
**/
void Delay_us(uint32_t xus)
{
    SysTick->CTRL = 0;                      // 关闭SysTick
    SysTick->LOAD = (9 * xus) - 1;          // 设置重装载值
    SysTick->VAL = 0x00;                    // 清空计数器
    SysTick->CTRL = 0x00000001;             // 启用SysTick(使用内核时钟)
    while(!(SysTick->CTRL & 0x00010000));   // 等待计数完成
    SysTick->CTRL = 0;                      // 关闭SysTick
}

/**
    ******************************************************************************
    * 函数名称：Delay_ms
    * 功能描述：毫秒级延时
    * 输入参数：xms - 延时时长(0~1864 ms)
    * 返回值  ：无
    * 详细说明：
    * - 通过循环调用Delay_us(1000)实现
    * - 存在函数调用开销，精度略低
    * 调用示例：
    *   Delay_ms(500); // 延时500ms
    ******************************************************************************
**/
void Delay_ms(uint32_t xms)
{
    while(xms--)
    {
        Delay_us(1000);  // 累计延时
    }
}

/**
    ******************************************************************************
    * 函数名称：Delay_s
    * 功能描述：秒级延时
    * 输入参数：xs - 延时时长(0~1 s)
    * 返回值  ：无
    * 详细说明：
    * - 通过循环调用Delay_ms(1000)实现
    * - 适用于非精确长延时
    * 注意事项：
    * - 长时间阻塞式延时影响系统实时性
    ******************************************************************************
**/
void Delay_s(uint32_t xs)
{
    while(xs--)
    {
        Delay_ms(1000);  // 累计延时
    }
}

/**
    ******************************************************************************
    * 函数名称：USART1_Config
    * 功能描述：配置USART1串口通信参数
    * 输入参数：baud - 波特率(如9600,115200等)
    * 返回值  ：无
    * 详细说明：
    * - 使用PA9(TX)和PA10(RX)
    * - 8位数据位，无校验，1位停止位
    * - 使能接收中断
    * - 配置NVIC中断优先级(最高)
    * 硬件连接：
    * - TX引脚需接上位机的RX
    * - RX引脚需接上位机的TX
    * 调用示例：
    *   USART1_Config(115200); // 配置为115200波特率
    ******************************************************************************
**/
void USART1_Config(u32 baud)
{
    USART_InitTypeDef USART_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    /* 开启USART1和GPIOA时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

    /* 配置PA9(TX)为复用推挽输出 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* 配置PA10(RX)为浮空输入 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* 配置USART1参数 */
    USART_InitStructure.USART_BaudRate = baud;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(USART1, &USART_InitStructure);

    /* 配置USART1中断 */
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* 使能接收中断 */
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

    /* 使能USART1 */
    USART_Cmd(USART1, ENABLE);
}

/**
    ******************************************************************************
    * 函数名称：USART1_IRQHandler
    * 功能描述：USART1中断服务函数
    * 输入参数：无
    * 返回值  ：无
    * 详细说明：
    * - 处理接收中断(USART_IT_RXNE)
    * - 读取数据并回传(回显功能)
    * - 等待发送完成标志(USART_FLAG_TC)
    * 注意事项：
    * - 中断服务函数应尽量简短
    * - 未处理其他错误中断
    ******************************************************************************
**/
void USART1_IRQHandler(void)
{
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  // 检查接收中断标志
    {
        u8 data = USART_ReceiveData(USART1);  // 读取接收数据
        USART_SendData(USART1, data);         // 回传数据
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); // 等待发送完成
    }
}

/**
    ******************************************************************************
    * 函数名称：USART1_SendByte
    * 功能描述：发送单个字节
    * 输入参数：Byte - 要发送的字节
    * 返回值  ：无
    * 详细说明：
    * - 使用阻塞方式等待发送完成
    * - 检查USART_FLAG_TXE标志位
    * 调用示例：
    *   USART1_SendByte(0x41); // 发送字符'A'
    ******************************************************************************
**/
void USART1_SendByte(u8 Byte)
{
    USART_SendData(USART1, Byte);  // 发送数据
    while(!USART_GetFlagStatus(USART1, USART_FLAG_TXE)); // 等待发送完成
}

/**
    ******************************************************************************
    * 函数名称：USART1_SendArray
    * 功能描述：发送字节数组
    * 输入参数：
    *   Array - 数组指针
    *   Length - 数组长度
    * 返回值  ：无
    * 调用示例：
    *   u8 arr[] = {0x41,0x42,0x43};
    *   USART1_SendArray(arr, 3); // 发送"ABC"
    ******************************************************************************
**/
void USART1_SendArray(u8 *Array, u16 Length)
{
    u16 i;
    for(i = 0; i < Length; i++){
        USART1_SendByte(Array[i]);  // 逐个发送数组元素
    }
}

/**
    ******************************************************************************
    * 函数名称：USART1_SendString
    * 功能描述：发送字符串
    * 输入参数：String - 字符串指针
    * 返回值  ：无
    * 详细说明：
    * - 以'\0'作为字符串结束标志
    * 调用示例：
    *   USART1_SendString("Hello"); // 发送"Hello"
    ******************************************************************************
**/
void USART1_SendString(char *String)
{
    u8 i;
    for(i = 0; String[i] != '\0'; i++){
        USART1_SendByte(String[i]);  // 逐个发送字符
    }
}

/**
    ******************************************************************************
    * 函数名称：USART1_Pow
    * 功能描述：计算x的y次方
    * 输入参数：
    *   x - 底数
    *   y - 指数
    * 返回值  ：计算结果
    * 调用示例：
    *   USART1_Pow(10,2); // 返回100
    ******************************************************************************
**/
u32 USART1_Pow(u32 x, u32 y)
{
    uint32_t Result = 1;
    while(y--)
    {
        Result *= x;
    }
    return Result;
}

/**
    ******************************************************************************
    * 函数名称：USART1_SendNumber
    * 功能描述：发送数字
    * 输入参数：
    *   Num - 要发送的数字
    *   Length - 数字位数
    * 返回值  ：无
    * 详细说明：
    * - 从高位到低位依次发送
    * - 不足位数前面补零
    * 调用示例：
    *   USART1_SendNumber(123,3); // 发送"123"
    ******************************************************************************
**/
void USART1_SendNumber(u32 Num, u8 Length)
{
    u8 i;
    for(i = 0; i < Length; i++){
        USART1_SendByte((Num / USART1_Pow(10, Length - i - 1) % 10) + '0');
    }
}

/**
    ******************************************************************************
    * 函数名称：fputc
    * 功能描述：重定向printf输出到串口
    * 输入参数：
    *   ch - 要输出的字符
    *   f - 文件指针(未使用)
    * 返回值  ：输出的字符
    * 详细说明：
    * - 需在工程设置中启用MicroLIB
    * - 支持标准printf格式化输出
    * 注意事项：
    * - 缓冲区有限，长字符串可能被截断
    ******************************************************************************
**/
int fputc(int ch, FILE *f)
{
    USART1_SendByte(ch);  // 发送字符到串口
    return ch;
}

/**
    ******************************************************************************
    * 函数名称：USART1_Printf
    * 功能描述：自定义printf函数
    * 输入参数：
    *   format - 格式化字符串
    *   ... - 可变参数
    * 返回值  ：无
    * 详细说明：
    * - 使用vsprintf实现格式化
    * - 最大支持100字符
    * 调用示例：
    *   USART1_Printf("Value=%d",123); // 输出"Value=123"
    ******************************************************************************
**/
void USART1_Printf(char *format, ...)
{
    char String[100];      // 缓冲区
    va_list arg;           // 可变参数列表
    va_start(arg, format); // 初始化可变参数
    vsprintf(String, format, arg); // 格式化字符串
    va_end(arg);           // 结束可变参数
    USART1_SendString(String); // 发送格式化后的字符串
}

/**
    ******************************************************************************
    * 函数名称：main
    * 功能描述：主函数
    * 输入参数：无
    * 返回值  ：int(实际不会返回)
    * 详细说明：
    * - 初始化外设
    * - 演示各种串口发送功能
    * - 进入主循环
    * 执行流程：
    * 1. 初始化LED、蜂鸣器、按键、串口
    * 2. 发送测试数据
    * 3. 进入主循环(可添加其他功能)
    ******************************************************************************
**/
int main(void)
{
    /* 硬件初始化 */
    LED_STD_Config();
    BEEP_STD_Config();
    KEY_STD_Config();
    USART1_Config(9600);  // 初始化串口(9600波特率)

    /* 测试数据 */
    u8 Array[] = {0x42, 0x43, 0x44, 0x45};  // "BCDE"
    u8 Length = sizeof(Array)/sizeof(Array[0]);

    /* 演示各种发送方式 */
    USART1_SendByte(0x41);           // 发送"A"
    USART1_SendString("\r\n");       // 发送换行
    USART1_SendArray(Array, Length); // 发送数组
    USART1_SendString("\r\n");
    USART1_SendString("hello world");// 发送字符串
    USART1_SendString("\r\n");
    USART1_SendNumber(321,3);       // 发送数字321
    USART1_SendString("\r\nNum4=");
    USART1_Printf("%d", 444);        // 使用自定义printf
    USART1_SendString("\r\n");
    
    /* 主循环 */
    while(1)
    {
        // 可添加其他功能代码
        // 例如: LED闪烁、按键检测等
    }
}

/*************************************END OF FILE**************************************/
